/*
 *  Class name: PlayerDetailDialogWindow.java
 *  Version: 1.0
 *  Date: 13.12.2013
 */
package cz.muni.fi.pa165.soccergamesarchive.restclient.gui.dialogs;

import cz.muni.fi.pa165.soccergamesarchive.businesslayer.dtos.PlayerDTO;
import cz.muni.fi.pa165.soccergamesarchive.businesslayer.dtos.TeamDTO;
import cz.muni.fi.pa165.soccergamesarchive.restclient.gui.SGAClientMainFrame;
import cz.muni.fi.pa165.soccergamesarchive.restclient.gui.components.enums.DialogWindowChoosedOption;
import cz.muni.fi.pa165.soccergamesarchive.restclient.gui.components.enums.Operation;
import cz.muni.fi.pa165.soccergamesarchive.restclient.gui.components.interfaces.ResultListenable;
import cz.muni.fi.pa165.soccergamesarchive.restclient.gui.components.pageablelist.items.PageableListItemButton;
import cz.muni.fi.pa165.soccergamesarchive.restclient.rest.entity.TeamRestClient;
import cz.muni.fi.pa165.soccergamesarchive.restclient.utils.JOptionDialogWindowHandler;
import cz.muni.fi.pa165.soccergamesarchive.restclient.utils.StringUtils;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JTextField;

import static cz.muni.fi.pa165.soccergamesarchive.restclient.utils.JOptionDialogWindowHandler.showErrorDialogWindow;
import static java.util.Collections.sort;

/**
 *
 * @author Michal Ďuriš
 */
public class PlayerDetailDialogWindow extends javax.swing.JDialog implements
        ResultListenable {

    private static final long serialVersionUID = 1L;

    private Operation state = Operation.NO_OP;

    //TODO: Rethink parent entity usage...
    private final PageableListItemButton<TeamDTO> parentEntity;
    private PageableListItemButton<TeamDTO> currentlyManagedParentEntity;
    private final PageableListItemButton<PlayerDTO> player;

    private final Map<Component, Component> inputComponentMap = new HashMap<>();

    /**
     * Creates new form PlayerDetailDialogWindow
     *
     * @param parentEntity
     * @param player
     * @param modalityType
     */
    public PlayerDetailDialogWindow(PageableListItemButton<TeamDTO> parentEntity,
                                    PageableListItemButton<PlayerDTO> player,
                                    ModalityType modalityType) {

        super(player.getParentList().getOwner(), modalityType);

        this.parentEntity = parentEntity;
        this.currentlyManagedParentEntity = parentEntity;
        this.player = player;

        initComponents();

        setLocationRelativeTo(getOwner());
    }

    public PlayerDetailDialogWindow init() {

        identifyState();
        updateDialogWindowToCurrentState();
        initInputComponentMap();

        return this;
    }

    private void identifyState() {
        if (player.getEntity().getId() == null) {
            state = Operation.ADD;
        } else {
            state = Operation.CHANGE;
        }
    }

    private void updateDialogWindowToCurrentState() {

        switch (state) {
            case ADD:
                setTitle("Add player");
                updateComponents();
                break;
            case CHANGE:
                setTitle("Change player");
                updateComponents();
                actionButton.setText("Change");
                break;
            default:
                showErrorDialogWindow(this, "Error",
                        "Unable to recognize operation. Closing the dialog. Sorry...");
                dispose();
        }
    }

    private void updateComponents() {
        firstNameTextField.setText(player.getEntity().getFirstName());
        lastNameTextField.setText(player.getEntity().getLastName());

        teamTextField
                .setText(currentlyManagedParentEntity.getEntity().getName());
        updateShirtNumberComponent();
    }

    private void updateShirtNumberComponent() {
        if (currentlyManagedParentEntity.getEntity().getId() != null) {

            TeamRestClient restClient = new TeamRestClient();
            try {
                if (restClient.connect()) {

                    List<Integer> shirtNumberList = restClient
                            .getAvailableShirtNumbers(
                                    currentlyManagedParentEntity.getEntity());

                    if (isShirtNumberChangeTime()) {
                        shirtNumberList.add(player.getEntity().getShirtNumber());
                        sort(shirtNumberList);
                    }

                    shirtNumberComboBox.setModel(getComboBoxModel(
                            shirtNumberList));

                    if (isShirtNumberChangeTime()) {
                        shirtNumberComboBox.setSelectedItem(
                                player.getEntity().getShirtNumber());
                    }

                } else {
                    showErrorDialogWindow(getOwner(),
                            "Error",
                            "Unable to connect to the server. Try again later.");
                    dispose();
                }
            } catch (RuntimeException ex) {
                System.err.println(
                        "Exception thrown in updateShirtNumber method -> " + ex);

                showErrorDialogWindow(getOwner(),
                        "Error",
                        "Unable to retrieve shirt numbers. Try again later.");
                dispose();
            } finally {
                restClient.disconnect();
            }
        }
    }

    private boolean isShirtNumberChangeTime() {
        return (state == Operation.CHANGE)
                && (player.getEntity().getShirtNumber() > 0)
                && isFromTheSameTeam();
    }

    private boolean isFromTheSameTeam() {
        return currentlyManagedParentEntity != null
                && currentlyManagedParentEntity.equals(parentEntity);
    }

    private DefaultComboBoxModel<Integer> getComboBoxModel(
            List<Integer> itemList) {

        DefaultComboBoxModel<Integer> boxModel = new DefaultComboBoxModel<>();

        for (Integer integer : itemList) {
            boxModel.addElement(integer);
        }

        return boxModel;
    }

    private void initInputComponentMap() {
        inputComponentMap.put(firstNameLabel, firstNameTextField);
        inputComponentMap.put(lastNameLabel, lastNameTextField);
        inputComponentMap.put(teamLabel, teamTextField);
        inputComponentMap.put(shirtNumberLabel, shirtNumberComboBox);
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        playerPanel = new javax.swing.JPanel();
        firstNameLabel = new javax.swing.JLabel();
        firstNameTextField = new javax.swing.JTextField();
        lastNameLabel = new javax.swing.JLabel();
        lastNameTextField = new javax.swing.JTextField();
        teamLabel = new javax.swing.JLabel();
        teamTextField = new javax.swing.JTextField();
        shirtNumberLabel = new javax.swing.JLabel();
        shirtNumberComboBox = new javax.swing.JComboBox<>();
        errorLabel = new javax.swing.JLabel();
        buttonsPanel = new javax.swing.JPanel();
        actionButton = new javax.swing.JButton();
        cancelButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        playerPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Player", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Arial", 1, 14), new java.awt.Color(0, 102, 255))); // NOI18N

        firstNameLabel.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        firstNameLabel.setText("First name:");

        firstNameTextField.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N

        lastNameLabel.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        lastNameLabel.setText("Last name:");

        lastNameTextField.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N

        teamLabel.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        teamLabel.setText("Team:");

        teamTextField.setEditable(false);
        teamTextField.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
        teamTextField.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                teamTextFieldMouseClicked(evt);
            }
            public void mouseEntered(java.awt.event.MouseEvent evt) {
                teamTextFieldMouseEntered(evt);
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
                teamTextFieldMouseExited(evt);
            }
        });

        shirtNumberLabel.setFont(new java.awt.Font("Arial", 0, 12)); // NOI18N
        shirtNumberLabel.setText("Shirt number:");

        shirtNumberComboBox.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N

        errorLabel.setFont(new java.awt.Font("Arial", 1, 12)); // NOI18N
        errorLabel.setForeground(new java.awt.Color(255, 0, 0));
        errorLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);

        javax.swing.GroupLayout playerPanelLayout = new javax.swing.GroupLayout(playerPanel);
        playerPanel.setLayout(playerPanelLayout);
        playerPanelLayout.setHorizontalGroup(
            playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(playerPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(playerPanelLayout.createSequentialGroup()
                        .addGroup(playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                            .addComponent(teamLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(shirtNumberLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(lastNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(firstNameLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(firstNameTextField, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 193, Short.MAX_VALUE)
                            .addComponent(lastNameTextField, javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(teamTextField)
                            .addGroup(javax.swing.GroupLayout.Alignment.LEADING, playerPanelLayout.createSequentialGroup()
                                .addComponent(shirtNumberComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addGap(0, 0, Short.MAX_VALUE)))))
                .addContainerGap())
        );
        playerPanelLayout.setVerticalGroup(
            playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(playerPanelLayout.createSequentialGroup()
                .addGroup(playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(firstNameLabel)
                    .addComponent(firstNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lastNameLabel)
                    .addComponent(lastNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(teamLabel)
                    .addComponent(teamTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(playerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(shirtNumberLabel)
                    .addComponent(shirtNumberComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(errorLabel))
        );

        getContentPane().add(playerPanel, java.awt.BorderLayout.CENTER);

        buttonsPanel.setPreferredSize(new java.awt.Dimension(250, 45));

        actionButton.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
        actionButton.setText("Add");
        actionButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                actionButtonActionPerformed(evt);
            }
        });

        cancelButton.setFont(new java.awt.Font("Arial", 0, 11)); // NOI18N
        cancelButton.setText("Cancel");
        cancelButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cancelButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout buttonsPanelLayout = new javax.swing.GroupLayout(buttonsPanel);
        buttonsPanel.setLayout(buttonsPanelLayout);
        buttonsPanelLayout.setHorizontalGroup(
            buttonsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, buttonsPanelLayout.createSequentialGroup()
                .addContainerGap(157, Short.MAX_VALUE)
                .addComponent(actionButton)
                .addGap(18, 18, 18)
                .addComponent(cancelButton)
                .addContainerGap())
        );
        buttonsPanelLayout.setVerticalGroup(
            buttonsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, buttonsPanelLayout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(buttonsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(cancelButton)
                    .addComponent(actionButton))
                .addContainerGap())
        );

        getContentPane().add(buttonsPanel, java.awt.BorderLayout.PAGE_END);

        getAccessibleContext().setAccessibleName("errorStatus");

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void actionButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_actionButtonActionPerformed
        handleActionButton();
    }//GEN-LAST:event_actionButtonActionPerformed

    private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
        dispose();
    }//GEN-LAST:event_cancelButtonActionPerformed

    private void teamTextFieldMouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_teamTextFieldMouseEntered
        if (state == Operation.CHANGE) {
            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        }
    }//GEN-LAST:event_teamTextFieldMouseEntered

    private void teamTextFieldMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_teamTextFieldMouseClicked
        handleChangeTeamAction();
    }//GEN-LAST:event_teamTextFieldMouseClicked

    private void teamTextFieldMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_teamTextFieldMouseExited
        if (state == Operation.CHANGE) {
            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }
    }//GEN-LAST:event_teamTextFieldMouseExited

    private void handleChangeTeamAction() {
        if (state == Operation.CHANGE) {
            TeamChooserDialogWindow teamChooser = new TeamChooserDialogWindow(
                    this, ModalityType.APPLICATION_MODAL);
            teamChooser.setVisible(true);

            if (teamChooser.getChoosedOption() == DialogWindowChoosedOption.OK) {
                PageableListItemButton<TeamDTO> selectedItem = teamChooser
                        .getSelectedItem();

                //TODO: Add some logger if null
                if (selectedItem != null && !selectedItem.equals(parentEntity)) {
                    currentlyManagedParentEntity = selectedItem;

                    updateComponents();
                }
            }
        }
    }

    private void handleActionButton() {
        switch (state) {
            case ADD:
                handleAddAction();
                break;
            case CHANGE:
                handleChangeAction();
                break;
            default:
                JOptionDialogWindowHandler.showErrorDialogWindow(this, "Error",
                        "Unable to recognize operation. Action terminated. Closing the dialog. Sorry...");
                dispose();
        }
    }

    private void handleAddAction() {

        if (validateUserInput()) {
            updateEntity(player);
            player.store(this);
        }
    }

    private void handleChangeAction() {

        if (validateUserInput()) {
            PageableListItemButton<PlayerDTO> newPlayer = player.getParentList()
                    .createEntityListItem();
            newPlayer.getEntity().setId(player.getEntity().getId());
            updateEntity(newPlayer);

            player.change(newPlayer, this);
        }
    }

    private boolean validateUserInput() {

        delegateNormalStateToComponents();

        StringBuilder errorMessage = new StringBuilder("<html>");
        boolean errorFlag = false;

        for (Map.Entry<Component, Component> entry
                : inputComponentMap.entrySet()) {

            if (entry.getKey() instanceof JLabel) {

                JLabel label = (JLabel) entry.getKey();

                if (entry.getValue() instanceof JTextField) {

                    JTextField textField = (JTextField) entry.getValue();

                    if (StringUtils.isBlank(textField.getText())) {
                        errorFlag = true;

                        errorMessage.append(label.getText()).append(
                                " cannot be empty !!! <br> ");
                        label
                                .setForeground(
                                        SGAClientMainFrame.ERROR_STATE_COLOR);
                    }
                }

                if (entry.getValue() instanceof JComboBox) {

                    JComboBox<?> comboBox = (JComboBox<?>) entry.getValue();

                    if (comboBox.getSelectedItem() == null) {
                        errorFlag = true;

                        errorMessage.append(label.getText()).append(
                                " don´t have selected value !!!");
                    }
                }
            }
        }

        if (errorFlag) {
            errorLabel.setText(errorMessage.toString());

            updateWindow();
        }

        return !errorFlag;
    }

    private void updateEntity(PageableListItemButton<PlayerDTO> entity) {
        entity.getEntity().setFirstName(firstNameTextField.getText());
        entity.getEntity().setLastName(lastNameTextField.getText());
        entity.getEntity().setTeam(currentlyManagedParentEntity.getEntity());
        entity.getEntity().setShirtNumber(
                (int) shirtNumberComboBox.getSelectedItem());
    }

    private void delegateNormalStateToComponents() {

        for (Component label : inputComponentMap.keySet()) {
            label.setForeground(SGAClientMainFrame.NORMAL_STATE_COLOR);
        }

        errorLabel.setText("");

        updateWindow();
    }

    @Override
    public void processSuccessResult() {
        JOptionDialogWindowHandler.showInfoDialogWindow(this, "Info",
                "Player was saved.");

        if (!isFromTheSameTeam()) {

            if (!player.getParentList().removeListItemFromParentList(player)) {
                JOptionDialogWindowHandler.showErrorDialogWindow(this, "Error",
                        "Unable to update local list of players. Sorry... :( ");
            }
        }

        parentEntity.getParentList().updateList();

        dispose();
    }

    @Override
    public void processFailResult() {
        JOptionDialogWindowHandler.showErrorDialogWindow(this, "Error",
                "Player save operation failed. Try again later.");
    }

    private void updateWindow() {

        Dimension actualSize = getSize();

        pack();

        if (!actualSize.equals(getPreferredSize())) {
            setPreferredSize(getSize());
        }
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton actionButton;
    private javax.swing.JPanel buttonsPanel;
    private javax.swing.JButton cancelButton;
    private javax.swing.JLabel errorLabel;
    private javax.swing.JLabel firstNameLabel;
    private javax.swing.JTextField firstNameTextField;
    private javax.swing.JLabel lastNameLabel;
    private javax.swing.JTextField lastNameTextField;
    private javax.swing.JPanel playerPanel;
    private javax.swing.JComboBox<Integer> shirtNumberComboBox;
    private javax.swing.JLabel shirtNumberLabel;
    private javax.swing.JLabel teamLabel;
    private javax.swing.JTextField teamTextField;
    // End of variables declaration//GEN-END:variables

}
